use yew::prelude::*;
use yew::services::fetch::FetchTask;
use itertools::Itertools;
use crate::ob::user::{SysUser, UserListPage};
use crate::service::user::UserService;
use crate::ob::ResultGraphql;
use crate::error::{Error, GraphqlError};

#[derive(Properties, Clone, PartialEq, Debug)]
pub struct Props {
    pub id: &'static str,
    pub title: &'static str,
    pub data: Option<SysUser>,
    pub call: Callback<Result<ResultGraphql<UserListPage>, Error>>
}

pub enum Action {
    Account(String),
    Password(String),
    Name(String),
    Position(String),
    Save,
    SaveDone(Result<ResultGraphql<UserListPage>, Error>)
}

pub struct UserDetailView {
    props: Props,
    service: UserService,
    task: Option<FetchTask>,
    save_done: Callback<Result<ResultGraphql<UserListPage>, Error>>,
    save_error: Option<GraphqlError>,
    link: ComponentLink<Self>
}

impl UserDetailView {
    /// 是追加还是修改
    fn is_modify(&self) -> bool {
        match &self.props.data {
            Some(o) if o.id.is_some() => true,
            _ => false
        }
    }
    /// 取绑定 from 的数据
    fn get_data(&self) -> SysUser {
        self.props.clone().data.unwrap_or(SysUser {
            id: None,
            account: None,
            password: None,
            name: None,
            version: None,
            position: None,
            createTime: None
        })
    }
    /// form html
    fn form(&self) -> Html {
        let (account, password, name, position) = {
            match &self.props.data {
                Some(o) => (
                    o.account.clone().unwrap_or(String::new()),
                    o.password.clone().unwrap_or(String::new()),
                    o.name.clone().unwrap_or(String::new()),
                    o.position.unwrap_or(0)
                ),
                None => (String::new(), String::new(), String::new(), 0)
            }
        };
        let (a, p, n, pos) = (
            self.link.callback(|e: InputData| Action::Account(e.value)),
            self.link.callback(|e: InputData| Action::Password(e.value)),
            self.link.callback(|e: InputData| Action::Name(e.value)),
            self.link.callback(|e: ChangeData| match e {
                ChangeData::Select(el) => Action::Position(el.value()),
                _ => Action::Position(String::new())
            })
        );

        let pwd_el = {
            if self.is_modify() { html! {} }
            else { html! {
                <div class="form-group">
                    <label class="control-label">{"密码"}</label>
                    <input value=password oninput=p
                        class=self.err_class("password") type="password" placeholder="登陆密码" />
                    <div class="text-danger">{self.err_content("password")}</div>
                </div>
            } }
        };

        crate::component::row(html! {
            <form>
                <div class="form-group">
                    <label class="control-label">{"账号"}</label>
                    <input value=account oninput=a
                        class=self.err_class("account") type="text" placeholder="登陆账号" />
                    <div class="text-danger">{self.err_content("account")}</div>
                </div>
                { pwd_el }
                <div class="form-group">
                    <label class="control-label">{"姓名"}</label>
                    <input value=name oninput=n
                        class=self.err_class("name") type="text" placeholder="人员姓名" />
                    <div class="text-danger">{self.err_content("name")}</div>
                </div>
                <div class="form-group">
                    <label class="control-label">{"职位"}</label>
                    <select id="_usrDrp" class=self.err_class("position") onchange=pos>
                        <option selected={true} value="0"></option>
                        {
                            for crate::USER_POS
                                .iter()
                                .sorted()
                                .map(|(k, v)| html! { <option selected={k==&position} value={k.to_string()}>{v}</option> })
                        }
                    </select>
                    <div class="text-danger">{self.err_content("position")}</div>
                </div>
            </form>
        })
    }
    /// modal html
    fn modal(&self) -> Html {
        let click = self.link.callback(|_| Action::Save);
        html! {
            <div class="modal fade" id={self.props.id} data-backdrop="static">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">{&self.props.title}</h5>
                        </div>
                        <div class="modal-body">
                        { self.form() }
                        </div>
                        <div class="modal-footer">
                            <button onclick=click class="btn btn-primary" type="button">{"保存"}</button>
                            <button class="btn btn-secondary" type="button" data-dismiss="modal">{"关闭"}</button>
                        </div>
                    </div>
                </div>
            </div>
        }
    }
    /// 保存
    fn save(&mut self) {
        let ob = self.get_data();

        if self.is_modify() {
            self.task = Some(self.service.update(ob, self.save_done.clone()))
        } else {
            self.task = Some(self.service.save(ob, self.save_done.clone()))
        }
    }
    /// 保存后回调
    fn save_done(&mut self, res: Result<ResultGraphql<UserListPage>, Error>) -> bool {
        self.task = None;
        match res {
            Ok(o) => {
                self.save_error = None;
                self.props.call.emit(Ok(o));
                crate::close_modal(self.props.id);
                crate::swl_message(true, "保存成功!", "");
                false
            },
            Err(e) => {
                match e {
                    Error::GraphqlError(x) => {
                        let s = &x.errors[0].message;
                        if !s.is_empty() { crate::swl_message(false, "保存失败", s) }
                        self.save_error = Some(x);
                    },
                    _ => ()
                }
                true
            }
        }
    }
    /// 表单验证消息
    fn err_content(&self, s: &str) -> String {
        self.save_error.as_ref()
            .and_then(|x| x.errors.get(0))
            .and_then(|x| x.extensions.as_ref())
            .and_then(|x| x.get(s))
            .and_then(|x| x.as_str())
            .unwrap_or(&String::new()).to_string()
    }
    /// 表单验证样式
    fn err_class(&self, s: &str) -> String {
        let z = self.save_error.as_ref()
            .and_then(|x| x.errors.get(0))
            .and_then(|x| x.extensions.as_ref())
            .and_then(|x| x.get(s));

        if z.is_some() { String::from("form-control is-invalid") }
        else { String::from("form-control") }
    }
}

impl Component for UserDetailView {
    type Message = Action;
    type Properties = Props;

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            save_done: link.callback(Action::SaveDone),
            props, link, task: None, save_error: None,
            service: UserService::new()
        }
    }

    fn update(&mut self, msg: Self::Message) -> bool {
        let mut dt = self.get_data();
        match msg {
            Action::Account(s) => dt.account = Some(s),
            Action::Password(s) => dt.password = Some(s),
            Action::Name(s) => dt.name = Some(s),
            Action::Position(s) => {
                dt.position = Some(s.parse::<u32>().unwrap_or(0));
                self.props.data = Some(dt.clone());
                return true;
            },
            Action::Save => self.save(),
            Action::SaveDone(res) => { return self.save_done(res) }
        }

        self.props.data = Some(dt.clone());

        false
    }

    fn change(&mut self, props: Self::Properties) -> bool {
        if self.props != props {
            self.props = props;
            self.save_error = None;
            crate::select_clean("_usrDrp");
            true
        } else {
            false
        }
    }

    fn view(&self) -> Html { self.modal() }
}